﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using CloudShot.Core.Interfaces;
using CloudShot.Core.Types;
using CloudShot.Core.Utils;
using CloudShot.ImageStorages;
using CloudShot.Interfaces;
using CloudShot.Properties;
using CloudShot.Services;
using CloudShot.Settings;
using CloudShot.Utils;
using DropboxImageStorage;

namespace CloudShot
{
	public partial class MainForm : Form
	{
		private const int UPDATE_INTERVAL_HOURS = 24;
		private const string DONATE_URL = @"http://cloudshot.codeplex.com/wikipage?title=Donate";

		private readonly HotkeyListener _hotkeyListener;
		private readonly RecentShotsQueue _recentShots = new RecentShotsQueue();
		private readonly CloudShotSettings _cloudShotSettings;
		private readonly ConfigurationService _configurationService = new ConfigurationService();

		private readonly List<IImageStorage> _storages = new List<IImageStorage>();

		private bool _isShottingStarted;

		private IImageStorage _activeStorage;
		private IImageStorage ActiveStorage
		{
			get { return _activeStorage; }
			set
			{
				_activeStorage = value;
				OnActiveStorageChanged();
			}
		}

		private string ProductAndVersion { get { return Application.ProductName + " " + Application.ProductVersion; } }

		public MainForm()
		{
			InitializeComponent();

			Icon = Resources.applicationIcon;
			treeIcon.Icon = Resources.applicationIcon;
			treeIcon.Text = ProductAndVersion;

			_hotkeyListener = new HotkeyListener(this);
			_cloudShotSettings = _configurationService.ReadApplicationSettings();

			InitStorages();
		}

		private void InitStorages()
		{
			_storages.Add(new FileSystemStorage());
			_storages.Add(new DropboxStorage());

			var pm = new PluginManager(AppHelper.PluginsPath);

			foreach (IImageStorage plugin in pm.GetPlugins())
			{
				_storages.Add(plugin);
			}

			//Load storages settings
			var storagesSettings = _cloudShotSettings.ImageStorageSettingsList.ToDictionary(v => v.Name);

			foreach (IImageStorage storage in _storages)
			{
				var repository = new SettingsRepository();

				//TODO 
				ImageStorageSettings settings;

				if (storagesSettings.TryGetValue(storage.Name, out settings))
				{
					foreach (ImageStorageSettingsItem item in settings.StorageSettings)
					{
						repository.SetSetting(item.Name, item.Value);
					}
				}

				storage.Initialize(repository);
			}

			//Set active storage
			ActiveStorage = _storages.FirstOrDefault(s => s.Name == _cloudShotSettings.ActiveStorageName);

			if (ActiveStorage == null)
			{
				ActiveStorage = _storages.First();
				_cloudShotSettings.ActiveStorageName = ActiveStorage.Name;// TODO надо ли тут записывать настройки?
			}

			////Load storages settings
			//_appSettings.Load();

			////Set active storage
			//ActiveStorage = _storages.FirstOrDefault(s => s.GetType() == settings.ActiveStorage);

			//if (ActiveStorage == null)
			//{
			//  ActiveStorage = _storages.First();
			//  _appSettings.ActiveStorage = ActiveStorage.GetType();
			//}

			InitStoragesMenu();
		}

		private void InitStoragesMenu()
		{
			if (_storages.Count < 2)
				return;

			int index = treeIcon.ContextMenuStrip.Items.IndexOf(mnuSettings);

			if (index == -1)
				return;

			//add storages to menu
			foreach (IImageStorage storage in _storages)
			{
				var item = new ToolStripMenuItem(storage.Name, storage.Logo);
				item.Tag = storage;
				item.Checked = storage == ActiveStorage;
				item.Click += OnStorageItemClick;

				treeIcon.ContextMenuStrip.Items.Insert(index++, item);
			}
			treeIcon.ContextMenuStrip.Items.Insert(index, new ToolStripSeparator());
		}

		//TODO: Maybe I have to register hotkey in Settings window? And ofcourse when application starts.
		private void SetupHotkey()
		{
			if (IsDisposed)
				return;

			if (!RegisterHotkey(ApplicationHotkeys.ShotHotkeyName, _cloudShotSettings.ShotHotkey, mnuShot, OnShot))
			{
				_cloudShotSettings.ShotHotkey = string.Empty;
				_configurationService.WriteApplicationSettings(_cloudShotSettings);
			}

			if (!RegisterHotkey(ApplicationHotkeys.RecordHotkeyName, _cloudShotSettings.RecordHotkey, mnuRecord, OnRecordVideo))
			{
				_cloudShotSettings.ShotHotkey = string.Empty;
				_configurationService.WriteApplicationSettings(_cloudShotSettings);
			}
		}

		private bool RegisterHotkey(string hotkeyName, string hotkey, ToolStripMenuItem menu, EventHandler callback)
		{
			// if hotkey is empty, that means we have to set no hotkey and we have to show in menu empty string in hotkey section.
			if (string.IsNullOrEmpty(hotkey) || _hotkeyListener.RegisterHotkey(hotkeyName, hotkey, callback))
			{
				menu.ShortcutKeyDisplayString = hotkey;
				return true;
			}

			string message = string.Format("Hotkey \"{0}\" used by another application.\nPlease select another hotkey in Settings.", hotkey);
			MessageBox.Show(message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

			return false;
		}

		#region Recent Shots

		private void FillRecentShotsMenu(IEnumerable<ShotInfo> shots)
		{
			mnuRecentShots.DropDownItems.Clear();

			foreach (ShotInfo recentShot in shots)
			{
				var menuItem = new ToolStripMenuItem
				{
					ImageScaling = ToolStripItemImageScaling.None,
					Text = recentShot.Name,
					Tag = recentShot,
					Image = recentShot.Thumbnail
				};

				menuItem.Click += OnRecentShotClick;
				mnuRecentShots.DropDownItems.Insert(0, menuItem);
			}

			mnuRecentShots.Enabled = mnuRecentShots.DropDownItems.Count > 0;

			recentShotsContainer.RecentShots = shots.ToList();
		}

		private void OnRecentShotClick(object sender, EventArgs e)
		{
			var menu = sender as ToolStripMenuItem;

			if (menu == null)
				return;

			var recentShot = menu.Tag as ShotInfo;

			if (recentShot == null)
				return;

			OpenInViewer(recentShot);

			if (_cloudShotSettings.IsCopyRecentShotPath)
			{
				if (SetClipboardData(recentShot, null))
					ShowNotification(recentShot, "Link copied to clipboard");
			}
		}

		private void AddRecentShot(ShotInfo shotInfo)
		{
			_recentShots.Add(shotInfo);

			FillRecentShotsMenu(_recentShots);

			_cloudShotSettings.RecentShotsList = _recentShots.Select(r => new RecentShotInfo { Name = r.Name, Path = r.Path }).ToArray();
			_configurationService.WriteApplicationSettings(_cloudShotSettings);
		}

		#endregion

		private void Shot(bool isVideo)
		{
			if (_isShottingStarted)
				return;

			if (ActiveStorage == null)
				return;

			try
			{
				_isShottingStarted = true;

				Image image = isVideo ? RecordVideo() : MakeScreenshot();
				var saveFormat = isVideo ? ImageSaveFormat.Gif : ActiveStorage.CurrentImageFormat;

				if (image == null)
					return;

				SaveScreenshot(image, saveFormat);
			}
			catch (Exception err)
			{
				ErrorsHelper.TraceError(err);
				ErrorsHelper.ShowError(err);
			}
			finally
			{
				_isShottingStarted = false;
			}
		}

		private Image MakeScreenshot()
		{
			string activeStorageName = _cloudShotSettings.IsShowActiveStorageName ? ActiveStorage.Name : null;
			ICapturer capturer = new ShotCapturer(activeStorageName, _cloudShotSettings.IsEnableSounds, Color.DarkSlateBlue);
			return capturer.Capture();
		}

		private Image RecordVideo()
		{
			string activeStorageName = _cloudShotSettings.IsShowActiveStorageName ? ActiveStorage.Name : null;
			ICapturer capturer = new RecordCapturer(activeStorageName, Color.DarkSlateGray, _cloudShotSettings);
			return capturer.Capture();
		}

		private async void SaveScreenshot(Image image, ImageSaveFormat saveFormat)
		{
			Clipboard.Clear();//clear old screenshot url from clipboard

			ShotInfo shotInfo = await ActiveStorage.SaveImage(image, AppHelper.GetFileName(saveFormat), saveFormat.GetImageFormat());

		    if (shotInfo == null)
		    {
                treeIcon.ShowBalloonTip(1, "Error", "Cannot get link to screenshot", ToolTipIcon.Error);
                return;
		    }

			if (_cloudShotSettings.IsOpenInBrowser)
			{
				OpenInViewer(shotInfo);
			}

			AddRecentShot(shotInfo);
			SetClipboardData(shotInfo, image);
			ShowNotification(shotInfo, "Shot saved to storage");

			image.Dispose();
		}

		private void ShowNotification(ShotInfo shotInfo, string text)
		{
			if (!_cloudShotSettings.IsShowNotifications)
				return;

			treeIcon.Tag = shotInfo;
			treeIcon.ShowBalloonTip(1, text, shotInfo.Path, ToolTipIcon.Info);
		}

		private void SetTrayTooltip()
		{
			const int maxTaryTextLenght = 63;
			string hotkey = string.IsNullOrEmpty(_cloudShotSettings.ShotHotkey) ? "Not set" : _cloudShotSettings.ShotHotkey;
			string treeIconText = string.Format("{0}{1}Hotkey: {2}{1}Storage: {3}", ProductAndVersion, Environment.NewLine, hotkey, ActiveStorage.Name);

			if (treeIconText.Length > maxTaryTextLenght)
				treeIconText = treeIconText.Substring(0, maxTaryTextLenght);

			treeIcon.Text = treeIconText;
		}

		private bool SetClipboardData(ShotInfo shot, Image screenshot)
		{
			//Debug.Print("Copy shot to clipboard: " + url);

			if (_cloudShotSettings.IsCopyUrl || _cloudShotSettings.IsCopyImage)
			{
				//Clipboard.Clear();
				var dataObject = new DataObject();
				if (_cloudShotSettings.IsCopyUrl && !string.IsNullOrEmpty(shot.Path)) dataObject.SetText(shot.Path);
				if (_cloudShotSettings.IsCopyImage && screenshot != null) dataObject.SetImage(screenshot);
				Clipboard.SetDataObject(dataObject, true, 4, 500);
				return true;
			}

			return false;
		}

		private void OpenInViewer(ShotInfo shotInfo)
		{
			if (ActiveStorage == null)
				return;

			try
			{
				Process.Start(shotInfo.Path);
			}
			catch (Exception err)
			{
				//RemoveRecentShot(shotInfo);//TODO продумать как это сделать
				ErrorsHelper.ShowError(err);
			}
		}

		private void ShowSettings()
		{
			mnuSettings.Enabled = false;
			_hotkeyListener.UnregisterAll();

			var settingsForm = new SettingsForm(_cloudShotSettings, _storages);

			settingsForm.SetActiveStorage(ActiveStorage);

			if (settingsForm.ShowDialog(this) == DialogResult.OK)
				ActiveStorage = settingsForm.ActiveStorage;

			SetupHotkey(); //в любом случае задаем хоткеи, т.к. они были стерты через ClearHotkeys
			SetTrayTooltip();
			mnuSettings.Enabled = true;
		}

		private void TryUpdateApplication()
		{
			ApplicationUpdater.InstallUpdateIfAvailable(this);
		}

		#region Event Handlers

		private void OnLoad(object sender, EventArgs e)
		{
			Visible = false;

			SetupHotkey();
			SetTrayTooltip();
			_recentShots.Load(_cloudShotSettings.RecentShotsList.Select(s => new ShotInfo { Name = s.Name, Path = s.Path }));//TODO maybe it is better to change method signature
			FillRecentShotsMenu(_recentShots);

			treeIcon.Visible = true;

			var interval = new TimeSpan(UPDATE_INTERVAL_HOURS, 0, 0);//check updates every UPDATE_INTERVAL_HOURS hours
			tmrUpdater.Interval = (int)interval.TotalMilliseconds;
			tmrUpdater.Enabled = true;
			tmrUpdater.Start();
			TryUpdateApplication();
		}

		private void OnShot(object sender, EventArgs e)
		{
			Shot(false);
		}

		private void OnRecordVideo(object sender, EventArgs e)
		{
			Shot(true);
		}

		private void OnMenuExitClick(object sender, EventArgs e)
		{
			Application.Exit();
		}

		private void OnTreeIconMouseDoubleClick(object sender, MouseEventArgs e)
		{
			if (mnuShot.Enabled)
			{
				Shot(false);
			}
			else
			{
				var form = OwnedForms.FirstOrDefault();
				if (form != null)
					form.Activate();
			}
		}

		private void OnMenuSettingsClick(object sender, EventArgs e)
		{
			ShowSettings();
		}

		private void OnMenuDonateClick(object sender, EventArgs e)
		{
			if (string.IsNullOrEmpty(DONATE_URL))
				return;

			Process.Start(DONATE_URL);
		}

		private void OnMenuAboutClick(object sender, EventArgs e)
		{
			mnuAbout.Enabled = false;

			using (var dialog = new AboutBox())
			{
				dialog.ShowDialog();
			}

			mnuAbout.Enabled = true;
		}

		private void OnTimerTick(object sender, EventArgs e)
		{
			TryUpdateApplication();
		}

		private void OnStorageItemClick(object sender, EventArgs e)
		{
			var item = sender as ToolStripMenuItem;

			if (item == null)
				return;

			var storage = item.Tag as IImageStorage;

			if (storage != null)
			{
				ActiveStorage = storage;
				_cloudShotSettings.ActiveStorageName = storage.Name;
				_configurationService.WriteApplicationSettings(_cloudShotSettings);
				SetTrayTooltip();
			}
		}

		private void OnActiveStorageChanged()
		{
			foreach (ToolStripItem item in treeIcon.ContextMenuStrip.Items)
			{
				var menuItem = item as ToolStripMenuItem;
				if (menuItem != null)
					menuItem.Checked = menuItem.Tag == ActiveStorage;
			}
		}

		private void OnTreeIconBalloonTipClicked(object sender, EventArgs e)
		{
			if (treeIcon.Tag is ShotInfo)
			{
				OpenInViewer((ShotInfo)treeIcon.Tag);
				treeIcon.Tag = null;
			}
		}

		#endregion

		private void OnTreeIconMouseClick(object sender, MouseEventArgs e)
		{
			//Location = Cursor.Position;
			//Visible = true;
			//WindowState = FormWindowState.Normal;
		}
	}
}